#pragma once

// #include <dinfra/bitmap.h>

#include <dinfra/mplf/tstack.h>
#include <dinfra/init_fin.h>

/* spobj_t* */

/* 按cacheline对齐的全局共享永久对象的分配(锁, 锁节点, )等，统一分配的目的是放在同一个页里面减少共享对象访问时的tlbmiss */
typedef struct spobj{
	struct{
		tsnode_t node;
	}ALIGN(HDEF_CACHE_LINE_BYTES);
}spobj_t;

typedef struct spslab{
	struct{
		/* 用于连接 */
		tsnode_t	slab_node;

		/* free索引 */
		word_t		fidx;
		
		/* free list */
		tstack_t	flist;
	}ALIGN(HDEF_CACHE_LINE_BYTES);
	spobj_t	 cacheline[];
}spslab_t;

typedef struct spslab_ctl{
	tstack_t	spslab_stk;
}spslab_ctl_t;

static ALWAYS_INLINE spslab_t *__spslab_init(void *ptr){
	spslab_t *slab = (spslab_t *)ptr;
	slab->fidx = 0;
	tstack_init(&slab->flist);
	return slab;
}

static ALWAYS_INLINE spslab_t *spslab_init_append(spslab_ctl_t *spslab_ctl, void *ptr){
	spslab_t *slab =  __spslab_init(ptr);
	tstack_push(&spslab_ctl->spslab_stk, &slab->slab_node);
	return slab;
}
#include <dbg.h>
static ALWAYS_INLINE spobj_t *spslab_alloc(spslab_t *sl, const size_t max_cacheline_nr, const size_t obj_cacheline_nr){
	spobj_t *ret = NULL;
	if (sl->flist.head){
		tsnode_t *poped = tstack_pop(&sl->flist);
		if (poped){
			// dbg_printf("[popped get]%p\n", poped);
			ret = tstack_entry(poped, spobj_t, node);
			return ret;
		}
	}
	if (sl->fidx <= (max_cacheline_nr - obj_cacheline_nr)){
		word_t idx = faa(&sl->fidx, obj_cacheline_nr);
		if (idx < max_cacheline_nr){
			ret = &sl->cacheline[idx];
			// dbg_printf("[faa get]%p, %ld\n", ret, idx);
			return ret;
		}
	}
	
	// if (!sl->flist.head){
	//	 if (sl->fidx <= (max_cacheline_nr - obj_cacheline_nr)){
	//		 word_t idx = faa(&sl->fidx, obj_cacheline_nr);
	//		 if (idx < max_cacheline_nr){
	//			 ret = &sl->cacheline[idx];
	//		 }
	//	 }
	//	 dbg_printf("[faa get]%p\n", ret);
	// }
	// else{
	//	 tsnode_t *poped = tstack_pop(&sl->flist);
	//	 if (poped){
	//		 ret = tstack_entry(poped, spobj_t, node);
	//	 }
	//	 dbg_printf("[popped]%p\n", poped);
	// }
	return ret;
}


static ALWAYS_INLINE void spslab_free_batch(spslab_t *sl, spobj_t *hd, spobj_t *tl){
	// dbg_printf("[pushed ret]%p\n", hd);
	__tstack_push_batch(&sl->flist, &hd->node, &tl->node);
}

static ALWAYS_INLINE void spslab_free(spslab_t *sl, spobj_t *hd){
	spslab_free_batch(sl, hd, hd);
}

#define DEF_SPSLAB_OBJ_STAIC(objname, objsize, slab_size) \
static /*SECTION(".bss.globalshare")*/ char objname##_pool_arr[align_up(slab_size, HDEF_CACHE_LINE_BYTES)] ALIGN(PAGE_SIZE) = {0};\
static spslab_t * objname##_pool = (spslab_t *) objname##_pool_arr;\
/*static word_t allocate_size = 0;*/\
static ALWAYS_INLINE spobj_t * objname##_spalloc(){ /*faa(&allocate_size, HDEF_CACHE_LINE_BYTES); printf("+%lu+\n", allocate_size); */\
return spslab_alloc( objname##_pool, (align_up(slab_size, HDEF_CACHE_LINE_BYTES)/HDEF_CACHE_LINE_BYTES) - 1, align_up(objsize, HDEF_CACHE_LINE_BYTES) / HDEF_CACHE_LINE_BYTES); }\
static ALWAYS_INLINE void objname##_spfree(spobj_t *hd, spobj_t *tl){ /*word_t cnt = 1; spobj_t *trav = hd; spobj_t *next = (spobj_t *)trav->node.next; while (next){ trav = next; next = (spobj_t *)trav->node.next;  cnt++;} */ \
	/*faa(&allocate_size, (0 - HDEF_CACHE_LINE_BYTES * cnt)); printf("-%lu-\n", allocate_size);*/ \
	spslab_free_batch( objname##_pool, hd, tl); }

#define DEF_SPSLAB_OBJ_STAIC_WITH_TLSCACHE(objname, ptlscache, objsize, slab_size) \
DEF_SPSLAB_OBJ_STAIC(objname, objsize, slab_size)\
static ALWAYS_INLINE spobj_t * objname##_cachealloc(){ spobj_t *ret; if (! (*(ptlscache)) ) { ret = objname##_spalloc();  /*assert(ret);*/ /*if (!ret) UNREACHABLE();*/ ASSUME(ret != NULL); return ret;} ret = *(ptlscache); *(ptlscache) = (spobj_t *) (*(ptlscache))->node.next; return ret;}	\
static ALWAYS_INLINE void objname##_cachefree(spobj_t *hd){ spobj_t *t = (*(ptlscache)); *(ptlscache) = hd; hd->node.next = (tsnode_t *)t;}\
CFLOWFIN_FUNC(objname){ spobj_t *hd = (*(ptlscache)); \
if (!hd) { /*printf("empty");*/ return;} \
*(ptlscache) = NULL; spobj_t *trav = hd; spobj_t *next = (spobj_t *)trav->node.next; \
while (next){ trav = next; next = (spobj_t *)trav->node.next; } \
objname##_spfree(hd, trav); }

// #define DEF_SPSLAB_OBJ_STATIC_TLSINIT(objname)\
// DECL_INIT(objname)\
// static TLS_INITIAL spobj_t *objname##_tlscache;\
// static void objname##_threadend(void *arg);\
// static pthread_key_t objname##_tlscache_key;\
// static void objname##_procinit(){ pthread_key_create( & objname##_tlscache_key, objname##_threadend);}\
// static void objname##_threadinit(){  pthread_setspecific( objname##_tlscache_key, (void *)&objname##_tlscache); };


// #define DEF_SPSLAB_OBJ_STAIC_WITH_TLSCACHEREAL(objname, objsize, slab_size) \
// DEF_SPSLAB_OBJ_STAIC(objname, objsize, slab_size)					   \
// DECL_INIT(objname)													  \
// static TLS_INITIAL spobj_t * objname##_tlscache;						\
// static ALWAYS_INLINE spobj_t * objname##_cachealloc(){ spobj_t *ret; if (! objname##_tlscache ) { ret = objname##_spalloc();  /*assert(ret);*/ /*if (!ret) UNREACHABLE();*/ ASSUME(ret != NULL); return ret;} ret = objname##_tlscache; objname##_tlscache = (spobj_t *) objname##_tlscache->node.next; return ret;}	\
// static ALWAYS_INLINE void objname##_cachefree(spobj_t *hd){ spobj_t *t = objname##_tlscache; objname##_tlscache = hd; hd->node.next = (tsnode_t *)t;}															  \
// static pthread_key_t objname##_tlscache_key;\
// static void objname##_threadend(void *arg){ spobj_t *hd = objname##_tlscache; \
// if (!hd) { /*printf("empty");*/ return;} \
// objname##_tlscache = NULL; spobj_t *trav = hd; spobj_t *next = (spobj_t *)trav->node.next; \
// while (next){ trav = next; next = (spobj_t *)trav->node.next; } \
// objname##_spfree(hd, trav); };\
// static void objname##_procinit(){ pthread_key_create( & objname##_tlscache_key, objname##_threadend);}												\
// static void objname##_threadinit(){  pthread_setspecific( objname##_tlscache_key, (void *)&objname##_tlscache); };
